package ro.upt.dcda.pricezone.s2.requests;

import java.util.ArrayList;
import java.util.List;

import javax.jms.JMSException;
import javax.jms.StreamMessage;

import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;

import ro.upt.dcda.pricezone.common.ConnectionFactory;
import ro.upt.dcda.pricezone.common.JMSMediator;
import ro.upt.dcda.pricezone.common.Server;
import ro.upt.dcda.pricezone.common.StoreData;
import ro.upt.dcda.pricezone.s2.S2;
import ro.upt.dcda.pricezone.s2.pojo.Store;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Point;

public class StoresRequest implements S2Request {

	private double longitude;
	private double latitude;
	private float range;
	private String name;
	private S2 server;
	private List<StoreData> retrievedStores;
	private Point customer;
	private boolean follow;

	public StoresRequest(StreamMessage request, S2 server) throws JMSException {
		follow = request.readString().equals("follow");
		name = request.readString();
		range = request.readFloat() / 100;
		longitude = request.readDouble();
		latitude = request.readDouble();
		this.server = server;
		System.out.println("Stores request:" + name + " " + range + " "
				+ longitude + " " + latitude);
	}

	public void work(StreamMessage message) throws Exception {
		retrievedStores = new ArrayList<>();
		getOwnStores();
		if (follow && server.isRangeOutsideServer(customer, range)) {
			getFromOtherServers();
		}
		System.out.println("retrieved " + retrievedStores.size() + " stores");
		message.writeInt(retrievedStores.size());
		for (StoreData store : retrievedStores) {
			message.writeInt(store.id);
			message.writeString(store.name);
			message.writeString(store.description);
			message.writeString(store.location);
			message.writeString(store.distance);
			message.writeString(store.server);
		}
	}

	private void getOwnStores() {
		Session session = ConnectionFactory.getFactory().openSession();
		try {
			customer = S2.FACTORY.createPoint(new Coordinate(longitude,
					latitude));
			Query query = session
					.createQuery("from Store where dwithin(location, :centerPoint, :range) = true");
			query.setParameter("centerPoint", customer);
			query.setParameter("range", range);
			@SuppressWarnings("unchecked")
			List<Store> stores = (List<Store>) query.list();
			// TODO: move into query
			for (Store store : stores) {
				if (name.isEmpty() || store.getName().contains(name)) {
					StoreData info = new StoreData();
					info.id = store.getId();
					info.name = store.getName();
					info.description = store.getDescription();
					info.location = store.getLocation().toString();
					info.distance = store.getLocation().distance(customer)
							* 100 + "";
					info.server = this.server.getName();
					retrievedStores.add(info);
				}
			}
		} catch (HibernateException e) {
			e.printStackTrace();
		} finally {
			session.close();
		}
	}

	private void getFromOtherServers() throws Exception {
		for (Server peer : JMSMediator.getMediator().getServers()) {
			StreamMessage msg = peer.createStreamMessage();
			msg.writeString("stores");
			msg.writeString("nofollow");
			msg.writeString(name);
			msg.writeFloat(range * 100);
			msg.writeDouble(longitude);
			msg.writeDouble(latitude);
			StreamMessage response = null;
			response = (StreamMessage) peer.getResponseFor(msg, 30000);
			if (response == null) {
				System.out.println("No response from peer "
						+ peer.getServerName());
				continue;
			}
			int nr = response.readInt();
			System.out.println("Receiving " + nr + " stores from peer "
					+ peer.getServerName());
			for (int i = 0; i < nr; i++) {
				StoreData store = new StoreData();
				store.id = response.readInt();
				store.name = response.readString();
				store.description = response.readString();
				store.location = response.readString();
				store.distance = response.readString();
				store.server = response.readString();
				retrievedStores.add(store);
			}
		}
	}
}
